/**
 * 请求配置接口
 */
export interface RequestConfig extends RequestInit {
  params?: Record<string, any>;
  timeout?: number;
  responseType?: "json" | "text" | "blob" | "formData";
}

function setRequestTimeout(config: RequestConfig) {
  const controller = new AbortController();
  if (config.signal) {
    config.signal.addEventListener("abort", () => controller.abort());
  }
  config.signal = controller.signal;
  return setTimeout(() => controller.abort(), config.timeout);
}

export type ResponseData<T = any> = Response & {
  data: T | Blob | FormData | string;
};

export class RequestFetchError extends Error {
  public response?: ResponseData;
  constructor(message: string, response?: ResponseData) {
    super(message);
    this.response = response;
  }
}

export function createRequest(baseConfig: {
  baseUrl?: string;
  timeout?: number;
  onRequest?: (config: RequestConfig) => void;
  onResponse?: (response: ResponseData) => void;
  onError?: (error: RequestFetchError) => void;
}) {
  const { baseUrl = "", timeout, onRequest, onResponse, onError } = baseConfig;

  // 默认请求配置
  const defaultConfig: RequestConfig = {
    headers: {
      "Content-Type": "application/json; charset=UTF-8",
    },
    credentials: "include",
    timeout,
  };

  const raw = async function <T = any>(url: string, config?: RequestConfig) {
    // 合并配置
    const finalConfig = {
      ...defaultConfig,
      ...config,
      headers: {
        ...defaultConfig.headers,
        ...config?.headers,
      },
    } as RequestConfig;
    // 处理自定义请求配置
    if (onRequest) onRequest(finalConfig);
    // 添加超时控制
    const timeoutId = finalConfig?.timeout ? setRequestTimeout(finalConfig) : 0;

    try {
      // 处理 query 参数
      if (finalConfig?.params) {
        const queryString = new URLSearchParams(finalConfig.params).toString();
        url = `${url}${url.includes("?") ? "&" : "?"}${queryString}`;
      }
      // fetch
      const response = (await fetch(
        `${baseUrl}${url}`,
        finalConfig
      )) as ResponseData<T>;
      // 处理响应类型
      const { responseType = "json" } = finalConfig;
      response.data = await response[responseType]();
      // 处理响应状态
      if (!response.ok)
        throw new RequestFetchError(response.statusText, response);
      // 处理响应
      if (onResponse) onResponse(response);
      return response;
    } catch (error) {
      // 错误处理
      const rfe =
        error instanceof RequestFetchError
          ? error
          : new RequestFetchError((error as Error).message);
      if (onError) onError(rfe);
      return Promise.reject(rfe);
    } finally {
      if (timeoutId && timeoutId > 0) {
        clearTimeout(timeoutId);
      }
    }
  };
  const request = async function <T>(url: string, config?: RequestConfig) {
    const response = await raw(url, config);
    return response.data as T;
  };
  request.raw = raw;
  return request;
}
